﻿using System.Collections.Generic;
using System.Xml;
using Jayden.Dll.Sc2;
using Jayden.Dll.Sc2.Descriptions;
using Jayden.Dll.Web.Events;
using Jayden.Dll.Web.HtmlControls;
using Jayden.Dll.Web.HtmlControls.Generics;
using Jayden.Dll.Web.Json;
using Jayden.Sc2;

namespace Jayden.Dll.Web.Controls.Sc2
{
	public class Starcraft2BuildOrderDiv : Div
	{
		public delegate void OnActionSuccess(HttpHandlerContext context, GameState state);
		public delegate void OnActionFailed(HttpHandlerContext context, GameState state);

		public class Starcraft2BuildOrderCommandHandler : HttpCommandHandler {

			public Starcraft2BuildOrderCommandHandler() : this("Sc2ExecuteAction", "Sc2UploadActionList", "Sc2DeleteActions") { }
			public Starcraft2BuildOrderCommandHandler(string executeActionCommand, string uploadActionCommand, string deleteActionsCommand)
				: base(executeActionCommand, uploadActionCommand, deleteActionsCommand)
			{
				RegisterEvent("ActionSuccess");
				RegisterEvent("ActionFailed");
			}

			public event OnActionSuccess ActionSuccess { add { AddEvent<OnActionSuccess>("ActionSuccess", value); } remove { RemoveEvent<OnActionSuccess>("ActionSuccess", value); } }
			public event OnActionFailed ActionFailed { add { AddEvent<OnActionFailed>("ActionFailed", value); } remove { RemoveEvent<OnActionFailed>("ActionFailed", value); } }

			protected override void ProcessCommand(HttpHandlerContext context, string command)
			{
				if (command == "Sc2ExecuteAction")
				{
					string race = context.Request["race"];
					string actionList = context.Request["actionList"];
					string action = context.Request["action"];

					RaceDictionary dictionary = new RaceDictionary();
					GameState state = new GameState(dictionary[race]);
					if (!string.IsNullOrWhiteSpace(actionList))
					{
						string[] actions = actionList.Split(',');
						foreach (string actionId in actions)
						{
							ActionDescription actionDescr = state.GetAction(actionId);
							if(actionDescr != null)
								state.Execute(actionDescr);
						}
					}
					if (!string.IsNullOrWhiteSpace(action))
					{
						ActionDescription actionDescr = state.GetAction(action);
						if (actionDescr != null)
						{
							if (state.Execute(actionDescr))
								Invoke("ActionSuccess", context, state);
							else
								Invoke("ActionFailed", context, state);
						}
					}
				}
				else if (command == "Sc2UploadActionList")
				{
					XmlDocument document = new XmlDocument();
					document.Load(context.Request.Files[0].InputStream);
					RaceDictionary dictionary = new RaceDictionary();
					ActionList actionList = new ActionList(document, dictionary);
					GameState state = new GameState(actionList[0].Entity.Race);
					state.Execute(actionList);
					Invoke("ActionSuccess", context, state);
				}
				else if (command == "Sc2DeleteActions")
				{
					string race = context.Request["race"];
					string actionList = context.Request["actionList"];
					
					RaceDictionary dictionary = new RaceDictionary();
					GameState state = new GameState(dictionary[race]);

					string[] indexesString = context.Request["actionIndexes"].Split(',');
					HashSet<int> indexes = new HashSet<int>();
					foreach (string indexString in indexesString)
						indexes.Add(int.Parse(indexString));

					string[] actions = actionList.Split(',');
					int index = 0;
					foreach (string action in actions)
					{
						ActionDescription description = state.GetAction(action);
						if (description != null && !indexes.Contains(index))
							state.Execute(description);
						index++;
					}
					Invoke("ActionSuccess", context, state);
				}
			}
		}

		private Form m_BuildOrderForm;
		private Input m_ActionList;
		private Select m_Race;
		private Select m_Action;
		private Button m_ExecuteAction;
		private Button m_DeleteActions;
		private BuildOrderTable m_BuildOrderTable;
		private A m_Download;

		private Form m_UploadActionListForm;
		private Input m_ActionListFile;
		private Button m_UploadActionList;

		public override void Load(System.Xml.XmlNode node, HtmlControlLoader loader)
		{
			base.Load(node, loader);
			if (Id == null)
				Id = "buildOrderDiv";
			m_BuildOrderForm = GetElementByServerId<Form>("buildOrderForm", true);
			m_ActionList = m_BuildOrderForm.GetFormElement<Input>("actionList");
			m_Race = m_BuildOrderForm.GetFormElement<Select>("race");
			m_Action = m_BuildOrderForm.GetFormElement<Select>("action");
			m_ExecuteAction = m_BuildOrderForm.GetFormElement<Button>("executeAction", "command");
			m_DeleteActions = m_BuildOrderForm.GetFormElement<Button>("deleteActions", "command");
			m_BuildOrderTable = GetElementByServerId<BuildOrderTable>("buildOrderTable", true);
			m_Download = GetElementByServerId<A>("download", true);

			m_UploadActionListForm = GetElementByServerId<Form>("uploadActionListForm", true);
			m_ActionListFile = m_UploadActionListForm.GetFormElement<Input>("actionListFile");
			m_UploadActionList = m_UploadActionListForm.GetFormElement<Button>("uploadActionList", "command");
		}

		private void SetActions(GameState state)
		{
			m_Action.Clear();
			m_Action.AddDefaultOption();
			foreach (ActionDescription action in state.Actions)
				m_Action.AddOption(action.Id, action.Name);
		}

		private void ProcessRequest(HttpHandlerContext context)
		{
			string race = context.Request["race"] ?? context.Uri["race"];
			string action = context.Request["action"];
			if (race != null)
				m_Race.SelectedValue = race;
			else
				race = m_Race.SelectedValue;

			if(!string.IsNullOrWhiteSpace(race))
			{
				RaceDictionary dictionary = new RaceDictionary();
				GameState state = new GameState(dictionary[race]);
				SetActions(state);
				if (action != null)
					m_Action.SelectedValue = action;
			}
		}

		public override void ProcessBinRequest(HttpHandlerContext context)
		{
			base.ProcessBinRequest(context);

			string actionList = context.Request["actionList"];
			string race = context.Request["race"] ?? context.Uri["race"];
			string[] actions = actionList.Split(',');

			RaceDictionary dictionary = new RaceDictionary();
			GameState state = new GameState(dictionary[race]);

			foreach (string action in actions)
			{
				ActionDescription d = state.GetAction(action);
				if (d != null)
					state.Execute(d);
			}
			context.Context.Response.Clear();
			context.Context.Response.ContentType = "text/xml";
			context.Context.Response.AppendHeader("Content-Disposition", "attachment; filename=actionList.xml");
			state.ActionList.XmlDocument.Save(context.Context.Response.OutputStream);
			context.Context.Response.End();
		}

		public override void ProcessHtmlRequest(HttpHandlerContext context)
		{
			Starcraft2BuildOrderCommandHandler buildOrderHandler = context.HandlerManager.GetHandler<Starcraft2BuildOrderCommandHandler>();
			buildOrderHandler.ActionFailed += new OnActionFailed(ActionFailed);
			buildOrderHandler.ActionSuccess += new OnActionSuccess(ActionSuccess);
			ProcessRequest(context);
			base.ProcessHtmlRequest(context);
		}

		public override void ProcessJsonRequest(HttpHandlerContext context)
		{
			Starcraft2BuildOrderCommandHandler buildOrderHandler = context.HandlerManager.GetHandler<Starcraft2BuildOrderCommandHandler>();
			buildOrderHandler.ActionFailed += new OnActionFailed(ActionFailed);
			buildOrderHandler.ActionSuccess += new OnActionSuccess(ActionSuccess);
			base.ProcessJsonRequest(context);
		}

		public override void ProcessJsRequest(HttpHandlerContext context)
		{
			string id = Id;
			if (!context.Js.Contains(id))
			{
				context.Js[id].AppendAjaxSubmit(m_BuildOrderForm, m_ExecuteAction, m_DeleteActions);
				context.Js[id].AppendAjaxHtmlHandler(m_Action, false, false);
				context.Js[id].AppendAjaxHtmlHandler(m_BuildOrderTable, false, false);
				context.Js[id].AppendAjaxHandler(@"
		if(data." + m_Race.Id + @" !== undefined) {
			$('#" + m_Race.Id + @"').val(data." + m_Race.Id + @");
		}
		if(data." + m_Download.Id + @" !== undefined) {
			$('#" + m_Download.Id + @"').attr('href', data." + m_Download.Id + @");
		}
		if(data.actionList !== undefined) {
			$('#" + m_ActionList.Id + @"').val(data.actionList);
		}
");
				context.Js[id].AppendOnInit(@"
	var $uploadForm = $('#" + m_UploadActionListForm.Id + @"');
	var $uploadFile = $('#" + m_ActionListFile.Id + @"');
	var $upload = $('#" + m_UploadActionList.Id + @"');

	var uploadComplete = function(e) {
		//text of response : e.target.responseText;
		try {
			// try to create a json object with the response
			var data = jQuery.parseJSON(e.target.responseText);
			// to use the common success handler
			JAYDEN.sfn.successAjax(data);
		} catch (err) {
			alert('Upload complete.');
		}
	}
	var uploadFailed = function(e) {
		alert('There was an error attempting to upload the file.');
	};
	var uploadCanceled = function(e) {
		alert('The upload has been canceled by the user or the browser dropped the connection.');
	};

	$uploadForm.submit(function(e) {
		var values = $uploadForm.serializeArray();
		var files = $uploadFile[0].files;
		if(!files)
			return true;
		var idx = 0;
		for(;idx<files.length;idx++)
			values.push({name: $uploadFile.attr('name') + '.files[' + idx + ']', value: files[idx] });
		values.push({name: $uploadFile.attr('name') + '.files.length', value: files.length });
		values.push({name: 'format', value: 'json' });
		values.push({name: 'command', value: $upload.val() });

		if(!JAYDEN.sfn.submitAjaxFormData(values, $uploadForm.attr('action'), $uploadForm.attr('method'), undefined, uploadComplete, uploadFailed, uploadCanceled)) {
			e.preventDefault();
			return false;
		} else {
			return true;
		}
	});
");
			}
			base.ProcessJsRequest(context);
		}

		public void AddHtmlToJson(JsonObject json, HtmlElement control, HttpHandlerContext context)
		{
			JsonObject obj;
			JsonValue value;
			if (!json.TryGetValue(control.Id, out value))
			{
				obj = new JsonObject();
				json.Add(control.Id, obj);
			}
			else
				obj = value as JsonObject;
			obj.Add("html", control.ToInnerHtml(context.Translator));
		}

		void ActionSuccess(HttpHandlerContext context, GameState state)
		{
			m_Race.SelectedValue = state.Race.Id;
			SetActions(state);
			Uri uri = new Uri(context.Request.Url);
			m_ActionList.Value = string.Join(",", state.ActionList);
			uri["format"] = "bin";
			uri["actionList"] = m_ActionList.Value;
			m_Download.HRef = uri.ToAbsoluteString(true);
			m_Action.SelectedValue = state.LastAction.Id;
			
			m_BuildOrderTable.AddHeaders();
			foreach (ActionDescription action in state.ActionList)
				m_BuildOrderTable.AddTr(action);

			if (context.Json != null)
			{
				AddHtmlToJson(context.Json, m_Action, context);
				AddHtmlToJson(context.Json, m_BuildOrderTable, context);
				context.Json.Add("actionList", string.Join(",", state.ActionList));
				context.Json.Add(m_Download.Id, m_Download.HRef);
				context.Json.Add(m_Race.Id, m_Race.SelectedValue);
			}

		}

		void ActionFailed(HttpHandlerContext context, GameState state)
		{
			m_Race.SelectedValue = state.Race.Id;
			SetActions(state);
			m_ActionList.Value = string.Join(",", state.ActionList);

			if (context.Json != null)
				AddHtmlToJson(context.Json, m_Action, context);
			else
			{
				Uri uri = new Uri(context.Request.Url);
				uri["format"] = "bin";
				uri["actionList"] = m_ActionList.Value;
				m_Download.HRef = uri.ToAbsoluteString(true);
				m_Action.SelectedValue = state.LastAction.Id;

				m_BuildOrderTable.AddHeaders();
				foreach (ActionDescription action in state.ActionList)
					m_BuildOrderTable.AddTr(action);
			}
		}
	}
}
